package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.audio.AudioNode;
import com.jme3.audio.AudioSource;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.collision.CollisionResults;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.font.Rectangle;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.ssao.SSAOFilter;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.system.AppSettings;
import java.util.Random;

/**
 * @author Tucker
 */
public class Main extends SimpleApplication
        implements ActionListener {

    private Spatial sceneModel;
    private BulletAppState bulletAppState;
    private RigidBodyControl landscape;
    private BetterCharacterControl player;
    private Node playerNode;
    private Vector3f walkDirection = new Vector3f();
    private boolean controls = false;
    private boolean left = false, right = false, up = false, down = false;
    private Vector3f camDir = new Vector3f();
    private Vector3f camLeft = new Vector3f();
    private AudioNode audio_step;
    private AudioNode music;
    private BitmapText hudText;
    private BitmapText timerText;
    private Random hint = new Random();
    private int hintNum = 0;
    private String[] hints = {"Hint:Look behind you", "Hint:If you push into the wall on a ledge, you can walk across it", "Hint:If you push into the edge of a platfrom, you'll climb on top", "Hint:Don't bump your head", "Hint:You have to touch the door to win"};
    private boolean startText;
    private String mission = "Misson:Enter the building through the door on the top";
    private String controlsInfo = "Controls:Use WASD to move. Press Space to jump. Press E for a hint. Press Q to dismiss a message.";
    private String winText = "Mission:COMPLETE Time:";
    int textCharIndex = 0;
    boolean controlText = false;
    boolean controlTextFirst = false;
    long time;
    boolean won;

    public static void main(String[] args) {
        AppSettings settings = new AppSettings(true);
        settings.setTitle("The Climb");
        settings.setSettingsDialogImage("Textures/menu.jpg");
        Main app = new Main();
        app.setSettings(settings);
        app.start();
    }

    public void simpleInitApp() {

        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);

        viewPort.setBackgroundColor(new ColorRGBA(0f, 0f, 0f, 1f));
        flyCam.setMoveSpeed(100);
        setUpKeys();

        sceneModel = assetManager.loadModel("Scenes/Game.j3o");
        sceneModel.setLocalScale(4f);

        CollisionShape sceneShape =
                CollisionShapeFactory.createMeshShape((Node) sceneModel);
        landscape = new RigidBodyControl(sceneShape, 0);
        sceneModel.addControl(landscape);

        playerNode = new Node();
        playerNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        playerNode.setLocalScale(0.06f);
        playerNode.setLocalTranslation(new Vector3f(-20, -56, 0));
        player = new BetterCharacterControl(1.5f, 6f, 1);
        player.setJumpForce(new Vector3f(8, 8, 8));
        player.setDuckedFactor(0.5f);

        playerNode.addControl(player);
        rootNode.attachChild(playerNode);
        bulletAppState.getPhysicsSpace().add(player);

        rootNode.attachChild(sceneModel);
        bulletAppState.getPhysicsSpace().add(sceneModel);

        AmbientLight ambient = new AmbientLight();
        ambient.setColor(ColorRGBA.White);
        rootNode.addLight(ambient);

        setDisplayFps(false);
        setDisplayStatView(false);
        
        guiFont = assetManager.loadFont("Interface/Fonts/SpecialElite.fnt");
        hudText = new BitmapText(guiFont, false);
        hudText.setBox(new Rectangle(0, settings.getHeight() - guiFont.getCharSet().getRenderedSize(), settings.getWidth(), settings.getHeight() - guiFont.getCharSet().getRenderedSize()));
        hudText.setSize(guiFont.getCharSet().getRenderedSize());
        hudText.setColor(ColorRGBA.White);
        hudText.setText("");
        hudText.setAlignment(BitmapFont.Align.Center);
        guiNode.attachChild(hudText);

        timerText = new BitmapText(guiFont, false);
        timerText.setBox(new Rectangle(0, guiFont.getCharSet().getRenderedSize(), settings.getWidth(), guiFont.getCharSet().getRenderedSize()));
        timerText.setSize(guiFont.getCharSet().getRenderedSize());
        timerText.setColor(ColorRGBA.White);
        timerText.setText("");
        timerText.setAlignment(BitmapFont.Align.Right);
        guiNode.attachChild(timerText);

        audio_step = new AudioNode(assetManager, "Sounds/Effects/Footstep.wav", false);
        audio_step.setPositional(true);
        audio_step.setLooping(false);
        audio_step.setVolume(2);
        rootNode.attachChild(audio_step);

        music = new AudioNode(assetManager, "Sounds/Music/Cataclysmic_Molten_Core.wav", false);
        music.setPositional(false);
        music.setLooping(true);
        music.setVolume(0.5f);
        rootNode.attachChild(music);
        music.play();

        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
SSAOFilter ssaoFilter = new SSAOFilter(12.94f, 43.92f, 0.33f, 0.61f);
fpp.addFilter(ssaoFilter);
viewPort.addProcessor(fpp);
        
        startText = true;
        new ConstantRateUpdate().start();
    }

    private void setUpKeys() {
        inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
        //inputManager.addMapping("Sneak", new KeyTrigger(KeyInput.KEY_F));
        inputManager.addMapping("Hint", new KeyTrigger(KeyInput.KEY_E));
        inputManager.addMapping("Dismiss", new KeyTrigger(KeyInput.KEY_Q));
        inputManager.addListener(this, "Left");
        inputManager.addListener(this, "Right");
        inputManager.addListener(this, "Up");
        inputManager.addListener(this, "Down");
        inputManager.addListener(this, "Jump");
        //inputManager.addListener(this, "Sneak");
        inputManager.addListener(this, "Hint");
        inputManager.addListener(this, "Dismiss");
    }

    public void onAction(String binding, boolean value, float tpf) {
        if (controls) {
            if (binding.equals("Left")) {
                if (value) {
                    left = true;
                } else {
                    left = false;
                }
            } else if (binding.equals("Right")) {
                if (value) {
                    right = true;
                } else {
                    right = false;
                }
            } else if (binding.equals("Up")) {
                if (value) {
                    up = true;
                } else {
                    up = false;
                }
            } else if (binding.equals("Down")) {
                if (value) {
                    down = true;
                } else {
                    down = false;
                }
            } else if (binding.equals("Jump")) {               
                    player.jump();
            } else if (binding.equals("Sneak")) {
                if (value) {
                    CollisionResults results = new CollisionResults();
                    // 2. Aim the ray from cam loc to cam direction.
                    Ray ray = new Ray(cam.getLocation(), cam.getDirection());
                    // 3. Collect intersections between Ray and Shootables in results list.
                    rootNode.collideWith(ray, results);
                    player.warp(results.getClosestCollision().getContactPoint());
                }
            } else if (binding.equals("Hint")) {
                if (hintNum == 0) {
                    hintNum = hint.nextInt(4) + 1;
                }

            }
        }
        if (binding.equals("Dismiss")) {
            if (value) {
                hudText.setText("");
                hintNum = 0;
            }
        }

    }

    @Override
    public void simpleUpdate(float tpf) {
        camDir = cam.getDirection().clone().multLocal(8f);
        camLeft = cam.getLeft().clone().multLocal(8f);
        camDir.y = 0;
        camLeft.y = 0;
        walkDirection.set(0, 0, 0);
        if (left) {
            walkDirection.addLocal(camLeft);
            if (audio_step.getStatus().equals(AudioSource.Status.Stopped) && player.isOnGround()) {
                audio_step.play();
            }
        }
        if (right) {
            walkDirection.addLocal(camLeft.negate());
            if (audio_step.getStatus().equals(AudioSource.Status.Stopped) && player.isOnGround()) {
                audio_step.play();
            }
        }
        if (up) {
            walkDirection.addLocal(camDir);
            if (audio_step.getStatus().equals(AudioSource.Status.Stopped) && player.isOnGround()) {
                audio_step.play();
            }
        }
        if (down) {
            walkDirection.addLocal(camDir.negate());
            if (audio_step.getStatus().equals(AudioSource.Status.Stopped) && player.isOnGround()) {
                audio_step.play();
            }
        }
        player.setWalkDirection(walkDirection);
            cam.setLocation(playerNode.getLocalTranslation().add(0, 5f, 0));
           
        if (playerNode.getLocalTranslation().distance(new Vector3f(-24, 55, -48)) < 4) {
            won = true;
            controls = false;
            up = false;
            down = false;
            left = false;
            right = false;
        }
        if (playerNode.getLocalTranslation().y < -90) {
            player.warp(new Vector3f(-20, -56, 0));
        }
    }

    private class ConstantRateUpdate extends Thread {

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (Exception ex) {
            }
            int toSeconds = 0;
            while (true) {
                if (startText) {
                    hudText.setText(hudText.getText() + mission.charAt(textCharIndex));
                    textCharIndex++;
                    if (textCharIndex >= mission.length()) {
                        startText = false;
                        controlText = true;
                        controlTextFirst = true;
                        textCharIndex = 0;

                    }
                }

                if (controlTextFirst) {
                    try {
                        Thread.sleep(3000);
                    } catch (Exception ex) {
                    }
                    hudText.setText("");
                    controlTextFirst = false;
                }

                if (controlText) {
                    hudText.setText(hudText.getText() + controlsInfo.charAt(textCharIndex));
                    textCharIndex++;
                    if (textCharIndex >= controlsInfo.length()) {
                        controlText = false;
                        textCharIndex = 0;
                        controls = true;

                    }
                }

                if (hintNum != 0) {
                    if (hudText.getText().length() > textCharIndex + 1) {
                        hudText.setText("");
                    }
                    hudText.setText(hudText.getText() + hints[hintNum].charAt(textCharIndex));
                    textCharIndex++;
                    if (textCharIndex >= hints[hintNum].length()) {
                        hintNum = 0;
                        textCharIndex = 0;


                    }
                }


                if (controls) {
                    toSeconds++;
                }
                fpsText.setText("" + toSeconds);

                if (controls && toSeconds == 8) {
                    timerText.setColor(ColorRGBA.White);
                }
                if (controls && toSeconds == 16) {
                    time++;
                    timerText.setText(longToTimeString(time));
                    timerText.setColor(ColorRGBA.Red);
                    toSeconds = 0;
                }
                try {
                    Thread.sleep(62, 500000);
                } catch (Exception ex) {
                }

                if (won) {
                    String end = winText + longToTimeString(time);
                    hudText.setText(hudText.getText() + end.charAt(textCharIndex));
                    textCharIndex++;
                    if (textCharIndex >= end.length()) {
                        won = false;

                    }
                }


            }
        }
    }

    public String longToTimeString(long seconds) {
        int hours = (int) (seconds / 3600);
        int minutes = (int) (seconds / 60) - (hours * 60);
        int secs = (int) seconds - (hours * 3600) - (minutes * 60);
        String hourText = "" + hours;
        String minuteText = "" + minutes;
        String secondText = "" + secs;
        if (hours < 10) {
            hourText = "0" + hours;
        }
        if (minutes < 10) {
            minuteText = "0" + minutes;
        }
        if (secs < 10) {
            secondText = "0" + secs;
        }
        return hourText + ":" + minuteText + ":" + secondText;
    }
}
